package craky.keeper;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.TableRowSorter;

import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;

import craky.component.CommonTableModel;
import craky.component.JImagePane;
import craky.componentc.JCButton;
import craky.componentc.JCCalendarComboBox;
import craky.componentc.JCCheckBox;
import craky.componentc.JCCheckBoxList;
import craky.componentc.JCCheckedComboBox;
import craky.componentc.JCComboBox;
import craky.componentc.JCLabel;
import craky.componentc.JCList;
import craky.componentc.JCMenuItem;
import craky.componentc.JCMessageBox;
import craky.componentc.JCNumberField;
import craky.componentc.JCPopupMenu;
import craky.componentc.JCScrollList;
import craky.componentc.JCScrollTable;
import craky.componentc.JCScrollText;
import craky.componentc.JCStringField;
import craky.componentc.JCTable;
import craky.componentc.JCTableHeader;
import craky.componentc.JCTextField;
import craky.keeper.allpay.AllPay;
import craky.keeper.allpay.AllPayDAO;
import craky.keeper.category.Category;
import craky.keeper.category.CategoryDAO;
import craky.keeper.user.User;
import craky.keeper.util.KeeperUtil;
import craky.layout.LineLayout;
import craky.util.IOUtil;

public abstract class KeeperPane extends JImagePane {
	private static final long serialVersionUID = 4659142438655902812L;
	protected static final String SHOW_STAT = "显示统计";
	private static final String[] COLUMNS_NAME = { "消费日期", "金额 ", "摘要", "类别",
			"明细", "备注", "入账时间", "入账用户" };
	protected KeeperApp keeper;
	protected KeeperMgr mgr;
	protected User currentUser;
	protected CommonTableModel<AllPay> model;
	protected List<AllPay> data;
	protected AllPay currentData;
	protected Action insertAction;
	protected Action modifyAction;
	protected Action deleteAction;
	protected Action submitAction;
	protected Action cancelAction;
	protected JCTable table;
	protected JCPopupMenu popup;
	protected JComponent left;
	protected JComponent right;
	protected JComponent top;
	protected JComponent bottom;
	protected JComponent center;
	protected JComponent ecFStartDate;
	protected JComponent ecFEndDate;
	protected JComponent ecFMinAmount;
	protected JComponent ecFMaxAmount;
	protected JComponent ecFSummary;
	protected JComponent ecFType;
	protected JComponent ecFDR;
	protected JComponent ecDate;
	protected JComponent ecAmount;
	protected JComponent ecSummary;
	protected JComponent ecType;
	protected JComponent ecDetail;
	protected JComponent ecRemark;
	protected JComponent ecRecordTime;
	protected JComponent ecRecorder;
	protected JCCalendarComboBox cpDate;
	protected JCCalendarComboBox cpFStartDate;
	protected JCCalendarComboBox cpFEndDate;
	protected JCNumberField cpAmount;
	protected JCNumberField cpFMinAmount;
	protected JCNumberField cpFMaxAmount;
	protected JCTextField cpSummary;
	protected JCTextField cpRecordTime;
	protected JCTextField cpRecorder;
	protected JCTextField cpFSummary;
	protected JCTextField cpFDR;
	protected JCComboBox cpType;
	protected JCCheckedComboBox cpFType;
	protected JCScrollText cpDetail;
	protected JCScrollText cpRemark;
	protected JCCheckBox btnShowLeft;
	protected JCCheckBox btnShowRight;
	private JCButton btnModify;
	private JCButton btnDelete;
	private JCButton btnInsert;
	private JCButton btnSubmit;
	private JCButton btnCancel;
	private JCList dateList;
	private Map<String, Category> categoryMap;
	protected Icon splitLeft;
	protected Icon splitRight;
	protected Icon emptyIcon;
	private boolean showDetail;
	private boolean showFind;
	private boolean showList;
	private boolean showStat;
	private boolean inited;
	private boolean modifying;
	private boolean loadImmediately;
	private int selectedIndex;
	protected SplitListener splitListener;

	public KeeperPane(KeeperApp keeper, KeeperMgr mgr) {
		this.keeper = keeper;
		this.mgr = mgr;
		this.categoryMap = keeper.getCategoryMap();
		this.currentUser = keeper.getCurrentUser();
		this.splitListener = new SplitListener();
		this.splitLeft = KeeperUtil.getIcon("split_left.png");
		this.splitRight = KeeperUtil.getIcon("split_right.png");
		this.emptyIcon = KeeperUtil.getIcon("split_empty.png");
		this.selectedIndex = -1;
		setName(getClass().getSimpleName());
		initActions();
		initUI();
		resetTypes();
		this.dateList.setSelectedIndex(this.dateList.getModel().getSize() - 1);
	}

	private void initActions() {
		this.insertAction = new AbstractAction("添加") {
			private static final long serialVersionUID = 5056698643009336063L;

			public void actionPerformed(ActionEvent e) {
				KeeperPane.this.insertOrModify(true);
			}
		};
		this.modifyAction = new AbstractAction("修改") {
			private static final long serialVersionUID = 8222836515926767063L;

			public void actionPerformed(ActionEvent e) {
				KeeperPane.this.insertOrModify(false);
			}
		};
		this.deleteAction = new AbstractAction("删除") {
			private static final long serialVersionUID = -4608405524916997614L;

			public void actionPerformed(ActionEvent e) {
				KeeperPane.this.delete();
			}
		};
		this.submitAction = new AbstractAction("确定") {
			private static final long serialVersionUID = 6989199497316998154L;

			public void actionPerformed(ActionEvent e) {
				KeeperPane.this.submit();
			}
		};
		this.cancelAction = new AbstractAction("取消") {
			private static final long serialVersionUID = 6989199497316998154L;

			public void actionPerformed(ActionEvent e) {
				KeeperPane.this.cancel();
			}
		};
		this.modifyAction.setEnabled(false);
		this.deleteAction.setEnabled(false);
	}

	private void createPopup(int purview) {
		if (purview <= 10) {
			this.popup = new JCPopupMenu();
			JCMenuItem miInsert = new JCMenuItem(this.insertAction);
			miInsert.setMnemonic('A');
			miInsert.setText(miInsert.getText() + "(A)");
			this.popup.add(miInsert);

			if (purview <= 0) {
				JCMenuItem miModify = new JCMenuItem(this.modifyAction);
				JCMenuItem miDelete = new JCMenuItem(this.deleteAction);
				miModify.setMnemonic('M');
				miModify.setText(miModify.getText() + "(M)");
				miDelete.setMnemonic('D');
				miDelete.setText(miDelete.getText() + "(D)");
				this.popup.add(miModify);
				this.popup.add(miDelete);
			}
		}
	}

	private void initUI() {
		int purview = this.currentUser.getPurview();
		this.btnModify = new JCButton(this.modifyAction);
		this.btnDelete = new JCButton(this.deleteAction);
		this.btnInsert = new JCButton(this.insertAction);
		this.btnSubmit = new JCButton(this.submitAction);
		this.btnCancel = new JCButton(this.cancelAction);
		this.btnModify.setPreferredSize(new Dimension(-1, 21));
		this.btnDelete.setPreferredSize(this.btnModify.getPreferredSize());
		this.btnInsert.setPreferredSize(this.btnModify.getPreferredSize());
		this.btnSubmit.setPreferredSize(this.btnModify.getPreferredSize());
		this.btnCancel.setPreferredSize(this.btnModify.getPreferredSize());
		this.btnSubmit.setVisible(false);
		this.btnCancel.setVisible(false);
		createPopup(purview);
		EmptyComponent centerParent = new EmptyComponent();
		setLayout(new BorderLayout());
		centerParent.setLayout(new BorderLayout(0, 3));
		createCenter();
		createTop();
		createBottom();
		createLeft();
		createRight();
		add(centerParent, "Center");

		if (this.center != null) {
			centerParent.add(this.center, "Center");
		}

		if (this.top != null) {
			centerParent.add(this.top, "North");
		}

		if (this.bottom != null) {
			centerParent.add(this.bottom, "South");
		}

		if (this.left != null) {
			add(this.left, "West");
		}

		if (this.right != null) {
			add(this.right, "East");
		}

		if (this.btnShowLeft != null) {
			this.btnShowLeft.setSelected(true);
		}
	}

	protected Criterion[] createCriterions(String type) {
		List<Criterion> list = new ArrayList<Criterion>();
		Calendar startDate = this.cpFStartDate.getDate();
		Calendar endDate = this.cpFEndDate.getDate();
		String minAmountStr = this.cpFMinAmount.getText();
		String maxAmountStr = this.cpFMaxAmount.getText();
		Number minAmount = (minAmountStr == null) || (minAmountStr.isEmpty()) ? null
				: this.cpFMinAmount.getNumber();
		Number maxAmount = (maxAmountStr == null) || (maxAmountStr.isEmpty()) ? null
				: this.cpFMaxAmount.getNumber();
		String summary = this.cpFSummary.getText();
		String dr = this.cpFDR.getText();

		if (startDate != null) {
			list.add(Restrictions.ge("date",
					new java.sql.Date(startDate.getTimeInMillis())));
		}

		if (endDate != null) {
			list.add(Restrictions.le("date",
					new java.sql.Date(endDate.getTimeInMillis())));
		}

		if (minAmount != null) {
			list.add(Restrictions.ge("amount",
					Float.valueOf(minAmount.floatValue())));
		}

		if (maxAmount != null) {
			list.add(Restrictions.le("amount",
					Float.valueOf(maxAmount.floatValue())));
		}

		if (type != null) {
			list.add(Restrictions.eq("type", type));
		} else if (this.currentUser.getPurview() >= 20) {
			list.add(Restrictions.ne("type", "额外"));
		}

		if ((summary != null) && (!summary.trim().isEmpty())) {
			list.add(Restrictions.like("summary", '%' + summary + '%'));
		}

		if ((dr != null) && (!dr.trim().isEmpty())) {
			dr = '%' + dr + '%';
			list.add(Restrictions.or(Restrictions.like("detail", dr),
					Restrictions.like("remark", dr)));
		}

		return list.toArray(new Criterion[list.size()]);
	}

	protected void resetTypes() {
		JCCheckBoxList checkList = this.cpFType.getCheckedList();
		boolean isVisitor = this.currentUser.getPurview() >= 20;
		boolean selectEmpty = checkList.isSelectedEmpty();
		Object oldSelType = this.cpType.getSelectedItem();
		List<Object> oldUnselFTypes = checkList.getAllItems();
		oldUnselFTypes.removeAll(this.cpFType.getSelectedItems());

		this.cpType.removeAllItems();
		this.cpFType.removeAllItems();

		for (Category category : this.categoryMap.values()) {
			String typeName = category.getName();

			if ((isVisitor) && (typeName.equals("额外")))
				continue;
			this.cpType.addItem(typeName);
			this.cpFType.addItem(typeName);
		}

		this.cpType.setSelectedItem(oldSelType);

		if (!selectEmpty) {
			List<Object> newSelFTypes = checkList.getAllItems();
			newSelFTypes.removeAll(oldUnselFTypes);
			this.cpFType.setSelectedItems(newSelFTypes);
		}
	}

	public void load() {
		this.data.clear();
		this.data.addAll(filterResult(AllPayDAO
				.getAllPays(createCriterions(this.mgr.getCurrentPane()
						.getType()))));
		this.model.refreshUI();
		this.mgr.resetTotal();
		refreshStat();
		this.inited = true;
	}

	protected List<AllPay> filterResult(List<AllPay> list) {
		return list;
	}

	public boolean isShowDetail() {
		return this.showDetail;
	}

	public void setShowDetail(boolean showDetail) {
		this.showDetail = showDetail;

		if (this.bottom != null) {
			this.bottom.setVisible(showDetail);
		}

		if (showDetail) {
			resetDetailValues();
		}
	}

	public boolean isShowFind() {
		return this.showFind;
	}

	public void setShowFind(boolean showFind) {
		this.showFind = showFind;

		if (this.top != null) {
			this.top.setVisible(showFind);
		}
	}

	public boolean isShowList() {
		return this.showList;
	}

	public void setShowList(boolean showList) {
		this.showList = showList;

		if (this.left != null) {
			this.left.setVisible(showList);
			revalidate();
		}
	}

	public boolean isShowStat() {
		return this.showStat;
	}

	public void setShowStat(boolean showStat) {
		boolean needRefresh = (showStat) && (!this.showStat);
		this.showStat = showStat;

		if (this.right != null) {
			this.right.setVisible(showStat);

			if (needRefresh) {
				refreshStat();
			}
		}
	}

	public boolean isInited() {
		return this.inited;
	}

	protected JComponent createLeft() {
		Vector<DateListItem> items = new Vector<DateListItem>();

		for (int i = 0; i < 12; i++) {
			Calendar calendar = Calendar.getInstance();
			calendar.add(2, i - 11);
			items.add(new DateListItem(calendar));
		}

		JCScrollList scList = new JCScrollList(items);
		this.dateList = scList.getList();
		this.dateList.setFixedCellHeight(30);
		this.dateList.setRendererOpaque(false);
		this.dateList.setFont(new Font("Arial", 1, 15));
		this.dateList.setForeground(Color.DARK_GRAY);
		this.dateList.setSelectionMode(0);
		scList.setHeaderVisible(true);
		scList.setHeaderText("快速查询");
		scList.getHeader().setHeaderHeight(25);
		scList.setImageOnly(true);
		scList.setAlpha(0.0F);
		scList.setPreferredSize(new Dimension(75, -1));
		this.dateList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				KeeperPane.this.quickFind(e);
			}
		});
		return this.left = scList;
	}

	protected JComponent createRight() {
		return this.right;
	}

	protected JComponent createTop() {
		int leftGap = this.btnShowLeft == null ? 0 : this.btnShowLeft
				.getPreferredSize().width;
		int rightGap = this.btnShowRight == null ? 0 : this.btnShowRight
				.getPreferredSize().width;
		this.top = new EmptyComponent();
		EmptyComponent ecFind = new EmptyComponent();
		Border border1 = new EmptyBorder(3, 0, 0, 3);
		Border border2 = new EmptyBorder(3, 0, 0, 0);
		JCButton btnFind = new JCButton("查找");
		JCLabel lbStartDate = new JCLabel("消费日期");
		JCLabel lbEndDate = new JCLabel("结束时间");
		JCLabel lbMinAmount = new JCLabel("金额()");
		JCLabel lbMaxAmount = new JCLabel("最大金额");
		JCLabel lbSummary = new JCLabel("摘要");
		JCLabel lbType = new JCLabel("类别");
		JCLabel lbDR = new JCLabel("明细/备注");
		this.cpFStartDate = new JCCalendarComboBox();
		this.cpFEndDate = new JCCalendarComboBox();
		this.cpFMinAmount = new JCNumberField(0, 2, 999999999.99000001D);
		this.cpFMaxAmount = new JCNumberField(0, 2, 999999999.99000001D);
		this.cpFType = new JCCheckedComboBox();
		this.cpFSummary = new JCStringField(25);
		this.cpFDR = new JCTextField();
		JComponent ecButton = createTitledComponent(null, btnFind,
				new EmptyBorder(5, 0, 2, 2), 0);

		btnFind.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				KeeperPane.this.load();
				KeeperPane.this.dateList.clearSelection();
			}
		});
		this.cpFMinAmount.setNonNegative(true);
		this.cpFMaxAmount.setNonNegative(true);
		this.cpFType.setText4SelectedAll("全部");
		btnFind.setFocusable(false);
		btnFind.setIconTextGap(0);
		btnFind.setIcon(KeeperUtil.getIcon("find.png"));
		btnFind.setImage(KeeperUtil.getImage("common_button_normal_bg.png",
				true));
		btnFind.setRolloverImage(KeeperUtil.getImage(
				"common_button_rollover_bg.png", true));
		btnFind.setPressedImage(KeeperUtil.getImage("common_button_pressed_bg",
				true));
		ecFind.setLayout(new BoxLayout(ecFind, 0));
		ecButton.setPreferredSize(new Dimension(57, -1));
		this.top.setBorder(new EmptyBorder(0, leftGap, 0, rightGap));
		this.top.setPreferredSize(new Dimension(-1, 27));
		this.top.setLayout(new BorderLayout());
		this.cpFStartDate.setPreferredSize(new Dimension(88, -1));
		this.cpFEndDate.setPreferredSize(new Dimension(88, -1));
		this.cpFMinAmount.setPreferredSize(new Dimension(50, -1));
		this.cpFMaxAmount.setPreferredSize(new Dimension(50, -1));
		this.cpFSummary.setPreferredSize(new Dimension(45, -1));
		this.cpFType.setPreferredSize(new Dimension(65, -1));
		this.cpFDR.setPreferredSize(new Dimension(45, -1));
		ecFind.add(this.ecFStartDate = createTitledComponent(lbStartDate,
				this.cpFStartDate, border2, 0));
		ecFind.add(this.ecFEndDate = createTitledComponent(lbEndDate,
				this.cpFEndDate, border1, 0));
		ecFind.add(this.ecFMinAmount = createTitledComponent(lbMinAmount,
				this.cpFMinAmount, border2, 0));
		ecFind.add(this.ecFMaxAmount = createTitledComponent(lbMaxAmount,
				this.cpFMaxAmount, border1, 0));
		ecFind.add(this.ecFSummary = createTitledComponent(lbSummary,
				this.cpFSummary, border1, 0));
		ecFind.add(this.ecFType = createTitledComponent(lbType, this.cpFType,
				border1, 0));
		ecFind.add(this.ecFDR = createTitledComponent(lbDR, this.cpFDR,
				border2, 0));
		this.top.add(ecFind, "Center");
		this.top.add(ecButton, "East");
		return this.top;
	}

	protected JComponent createBottom() {
		int leftGap = this.btnShowLeft == null ? 0 : this.btnShowLeft
				.getPreferredSize().width;
		int rightGap = this.btnShowRight == null ? 0 : this.btnShowRight
				.getPreferredSize().width;
		int purview = this.currentUser.getPurview();
		this.bottom = new EmptyComponent();
		this.bottom.setBorder(new CompoundBorder(new EmptyBorder(0, leftGap, 0,
				rightGap), new LineBorder(new Color(84, 165, 213))));
		this.bottom.setPreferredSize(new Dimension(-1, 77));
		this.bottom.setLayout(new BorderLayout());
		this.bottom.add(createDetailComponent(), "Center");

		if (purview <= 10) {
			EmptyComponent buttonContainer = new EmptyComponent();
			buttonContainer.setLayout(new LineLayout(3, 0, 5, 0, 3, 11, 11, 1));
			buttonContainer.setPreferredSize(new Dimension(85, -1));
			buttonContainer.add(this.btnSubmit, "EedFill");

			if (purview <= 0) {
				buttonContainer.add(this.btnDelete, "EedFill");
				buttonContainer.add(this.btnModify, "EedFill");
			}

			buttonContainer.add(this.btnInsert, "EedFill");
			buttonContainer.add(this.btnCancel, "EedFill");
			this.bottom.add(buttonContainer, "East");
		}

		return this.bottom;
	}

	@SuppressWarnings("rawtypes")
	protected JComponent createCenter() {
		Class[] columnsClass = { java.sql.Date.class, String.class,
				String.class, String.class, String.class, String.class,
				Timestamp.class, String.class };
		String[] getMethodsName = { "getDate", "getAmountString", "getSummary",
				"getType", "getDetail", "getRemark", "getRecordTime",
				"getRecorder" };
		String[] setMethodsName = { "setDate", "setAmountString", "setSummary",
				"setType", "setDetail", "setRemark", "setRecordTime",
				"setRecorder" };
		int[] columnsPreferredWidth = { 70, 60, 80, 50, 120, 120, 150, 60 };
		EmptyComponent ec = new EmptyComponent();
		JCScrollTable scTable = new JCScrollTable();
		this.table = scTable.getTable();
		this.data = new ArrayList<AllPay>();
		this.model = new CommonTableModel<AllPay>(this.table, COLUMNS_NAME,
				columnsClass, getMethodsName, setMethodsName, this.data);
		TableRowSorter<CommonTableModel<AllPay>> sorter = new TableRowSorter<CommonTableModel<AllPay>>(
				this.model);

		scTable.setAlpha(0.0F);
		scTable.setColumnControlEnabled(false);
		createLeftSplitButton();
		createRightSplitButton();
		ec.setLayout(new BorderLayout());
		ec.add(scTable, "Center");
		this.table.setModel(this.model);
		this.table.setDragEnabled(false);
		this.table.setSelectionMode(0);
		this.table.setRowSorter(sorter);
		this.table.setRendererOpaque(false);
		((JCTableHeader) this.table.getTableHeader()).setShowPopupMenu(false);
		this.table.getSelectionModel().addListSelectionListener(
				new ListSelectionListener() {
					public void valueChanged(ListSelectionEvent e) {
						KeeperPane.this.whenSelectionChanged(e);
					}
				});
		this.table.addMouseListener(new MouseAdapter() {
			@Override
			public void mousePressed(MouseEvent e) {
				KeeperPane.this.select(e);
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				KeeperPane.this.showPopupMenu(e);
			}
		});
		sorter.setComparator(1, new Comparator() {
			@SuppressWarnings("unused")
			public int compare(String str1, String str2) {
				try {
					float num1 = KeeperConst.AMOUNT_FORMAT.parse(str1)
							.floatValue();
					float num2 = KeeperConst.AMOUNT_FORMAT.parse(str2)
							.floatValue();
					return (int) ((num1 - num2) * 100.0F);
				} catch (Exception e) {
				}
				return str1.compareTo(str2);
			}

			public int compare(Object o1, Object o2) {

				return 0;
			}
		});
		for (int i = 0; i < columnsPreferredWidth.length; i++) {
			this.table.getColumnModel().getColumn(i)
					.setPreferredWidth(columnsPreferredWidth[i]);
		}

		if (this.btnShowLeft != null) {
			ec.add(this.btnShowLeft, "West");
		}

		if (this.btnShowRight != null) {
			ec.add(this.btnShowRight, "East");
		}

		return this.center = ec;
	}

	private JComponent createDetailComponent() {
		EmptyComponent ec = new EmptyComponent();
		EmptyComponent firstRow = new EmptyComponent();
		EmptyComponent secondRow = new EmptyComponent();
		JCLabel lbDate = new JCLabel("消费日期");
		JCLabel lbAmount = new JCLabel("金额(元)");
		JCLabel lbSummary = new JCLabel("摘要");
		JCLabel lbType = new JCLabel("类别");
		JCLabel lbRecordTime = new JCLabel("入账时间");
		JCLabel lbRecorder = new JCLabel("入账用户");
		JCLabel lbDetail = new JCLabel("明细");
		JCLabel lbRemark = new JCLabel("备注");
		Border border = new EmptyBorder(0, 0, 0, 5);
		this.cpDate = new JCCalendarComboBox();
		this.cpAmount = new JCNumberField(0, 2, 999999999.99000001D);
		this.cpSummary = new JCStringField(25);
		this.cpType = new JCComboBox();
		this.cpRecordTime = new JCTextField();
		this.cpRecorder = new JCTextField();
		this.cpDetail = new JCScrollText();
		this.cpRemark = new JCScrollText();

		this.cpAmount.setNonNegative(true);
		this.cpDetail.getEditor().setLineWrap(true);
		this.cpRemark.getEditor().setLineWrap(true);
		firstRow.setLayout(new BoxLayout(firstRow, 0));
		firstRow.setPreferredSize(new Dimension(-1, 25));
		secondRow.setLayout(new GridLayout(1, 2));
		firstRow.add(this.ecDate = createTitledComponent(lbDate, this.cpDate,
				border, 3));
		firstRow.add(this.ecAmount = createTitledComponent(lbAmount,
				this.cpAmount, border, 3));
		firstRow.add(this.ecSummary = createTitledComponent(lbSummary,
				this.cpSummary, border, 3));
		firstRow.add(this.ecType = createTitledComponent(lbType, this.cpType,
				border, 3));
		firstRow.add(this.ecRecordTime = createTitledComponent(lbRecordTime,
				this.cpRecordTime, border, 3));
		firstRow.add(this.ecRecorder = createTitledComponent(lbRecorder,
				this.cpRecorder, null, 3));
		firstRow.setPreferredSize(new Dimension(-1, 24));
		secondRow.add(this.ecDetail = createTitledComponent(lbDetail,
				this.cpDetail, border, 3));
		secondRow.add(this.ecRemark = createTitledComponent(lbRemark,
				this.cpRemark, new EmptyBorder(0, 0, 0, 1), 3));
		ec.setLayout(new BorderLayout(0, 5));
		ec.setBorder(new EmptyBorder(3, 5, 2, 2));
		ec.add(firstRow, "North");
		ec.add(secondRow, "Center");
		this.ecDate.setPreferredSize(new Dimension(80, -1));
		this.ecAmount.setPreferredSize(new Dimension(50, -1));
		this.ecSummary.setPreferredSize(new Dimension(45, -1));
		this.ecType.setPreferredSize(new Dimension(28, -1));
		this.ecRecordTime.setPreferredSize(new Dimension(105, -1));
		this.ecRecorder.setPreferredSize(new Dimension(40, -1));
		this.cpRecordTime.setEditable(false);
		this.cpRecordTime.setFocusable(false);
		this.cpRecorder.setEditable(false);
		this.cpRecorder.setFocusable(false);
		return ec;
	}

	protected JCCheckBox createLeftSplitButton() {
		this.btnShowLeft = new JCCheckBox();
		this.btnShowLeft.setPreferredSize(new Dimension(6, -1));
		this.btnShowLeft.setIcon(this.emptyIcon);
		this.btnShowLeft.setSelectedIcon(this.emptyIcon);
		this.btnShowLeft.setRolloverIcon(this.splitLeft);
		this.btnShowLeft.setRolloverSelectedIcon(this.splitRight);
		this.btnShowLeft.setPressedIcon(this.splitLeft);
		this.btnShowLeft.setPressedSelectedIcon(this.splitRight);
		this.btnShowLeft.setFocusable(false);
		this.btnShowLeft.setToolTipText("显示月份");
		this.btnShowLeft.addItemListener(this.splitListener);
		return this.btnShowLeft;
	}

	protected JCCheckBox createRightSplitButton() {
		return null;
	}

	private JComponent createTitledComponent(JComponent titleComponent,
			JComponent component, Border border, int hgap) {
		EmptyComponent ec = new EmptyComponent();
		ec.setBorder(border);
		ec.setLayout(new BorderLayout(hgap, 0));
		ec.add(component, "Center");

		if (titleComponent != null) {
			ec.add(titleComponent, "West");
		}

		return ec;
	}

	private void quickFind(ListSelectionEvent e) {
		if (this.currentData == null) {
			resetDetailValues();
		}

		if ((!e.getValueIsAdjusting())
				&& (this.dateList.getSelectedIndex() >= 0)) {
			DateListItem item = (DateListItem) this.dateList.getSelectedValue();
			this.cpFStartDate.setDate(item.getStartDate());
			this.cpFEndDate.setDate(item.getEndDate());
			this.cpFMaxAmount.setText(null);
			this.cpFMinAmount.setText(null);
			this.cpFSummary.setText(null);
			this.cpFType.selecteAll();
			this.cpFDR.setText(null);

			if (this.loadImmediately) {
				load();
			} else {
				this.loadImmediately = true;
			}
		}
	}

	private void resetDetailValues() {
		if (this.bottom.isVisible()) {
			if (this.currentData == null) {
				clearValues();
			} else {
				fillValues();
			}

			resetTextStatus(false);
			resetButtonStatus(false);
		}
	}

	private void clearValues() {
		this.cpDate.setDate(null);
		this.cpAmount.setText(null);
		this.cpSummary.setText(null);
		this.cpType.setSelectedIndex(-1);
		this.cpDetail.getEditor().setText(null);
		this.cpRemark.getEditor().setText(null);
		this.cpRecordTime.setText(null);
		this.cpRecorder.setText(null);
	}

	private void fillValues() {
		Calendar date = Calendar.getInstance();
		date.setTime(this.currentData.getDate());
		this.cpDate.setDate(date);
		this.cpAmount.setText(this.currentData.getAmountString().replaceAll(
				",", ""));
		this.cpSummary.setText(this.currentData.getSummary());
		this.cpType.setSelectedItem(this.currentData.getType());
		this.cpDetail.getEditor().setText(this.currentData.getDetail());
		this.cpRemark.getEditor().setText(this.currentData.getRemark());
		this.cpRecordTime.setText(this.currentData.getRecordTime().toString());
		this.cpRecorder.setText(this.currentData.getRecorder());
	}

	private void resetTextStatus(boolean editable) {
		this.cpDate.setEditableAll(editable);
		this.cpAmount.setEditable(editable);
		this.cpSummary.setEditable(editable);
		this.cpType.setEditableAll(editable);
		this.cpDetail.setEditable(editable);
		this.cpRemark.setEditable(editable);
	}

	private void resetButtonStatus(boolean modifying) {
		this.modifying = modifying;
		this.btnSubmit.setVisible(modifying);
		this.btnCancel.setVisible(modifying);
		this.btnInsert.setVisible(!modifying);
		this.btnModify.setVisible(!modifying);
		this.btnDelete.setVisible(!modifying);
	}

	private void select(MouseEvent e) {
		int row = this.table.rowAtPoint(e.getPoint());

		if ((row >= 0) && (SwingUtilities.isRightMouseButton(e))) {
			this.table.getSelectionModel().setSelectionInterval(row, row);
		} else if ((row < 0) && (!SwingUtilities.isMiddleMouseButton(e))) {
			if (this.selectedIndex >= 0) {
				this.table.clearSelection();
			} else {
				resetDetailValues();
			}
		}
	}

	private void showPopupMenu(MouseEvent e) {
		if ((this.popup != null) && (SwingUtilities.isRightMouseButton(e))) {
			this.popup.show((Component) e.getSource(), e.getX(), e.getY());
		}
	}

	private void whenSelectionChanged(ListSelectionEvent e) {
		this.currentData = (this.table.getSelectedRow() < 0 ? null : this.model
				.getSelectedRowData());
		boolean selectedEmpty = (this.selectedIndex = this.table
				.getSelectedRow()) < 0;
		this.modifyAction.setEnabled(!selectedEmpty);
		this.deleteAction.setEnabled(!selectedEmpty);
		resetDetailValues();
	}

	protected void resetRowValues(AllPay pay) {
		pay.setDate(new java.sql.Date(this.cpDate.getDate().getTimeInMillis()));
		pay.setAmount(this.cpAmount.getNumber().floatValue());
		pay.setDetail(this.cpDetail.getEditor().getText());
		pay.setRemark(this.cpRemark.getEditor().getText());
		pay.setSummary(this.cpSummary.getText());
	}

	public void cancel() {
		this.btnSubmit.setActionCommand(null);
		resetDetailValues();
	}

	protected boolean checkInput() {
		String info = null;
		JComponent editor = null;

		if (this.cpDate.getDate() == null) {
			info = "消费日期不能为空";
			editor = this.cpDate;
		} else {
			Number amount;
			if (((amount = this.cpAmount.getNumber()) == null)
					|| (amount.floatValue() == 0.0F)) {
				info = "金额不能为空且必须大0";
				editor = this.cpAmount;
			} else {
				String summary;
				if (((summary = this.cpSummary.getText()) == null)
						|| (summary.trim().isEmpty())) {
					info = "摘要不能为空";
					editor = this.cpSummary;
				} else if ((this.ecType.isVisible())
						&& (this.cpType.getSelectedIndex() < 0)) {
					info = "类别不能为空";
					editor = this.cpType;
				}
			}
		}
		if (info != null) {
			editor.requestFocus();
			JCMessageBox.createInformationMessageBox(
					this.keeper.getMainFrame(), "输入错误", info).open();
			return false;
		}

		return true;
	}

	protected boolean checkFilter(AllPay data) {
		Calendar startDate = this.cpFStartDate.getDate();
		Calendar endDate = this.cpFEndDate.getDate();
		String minAmountStr = this.cpFMinAmount.getText();
		String maxAmountStr = this.cpFMaxAmount.getText();
		Number minAmount = (minAmountStr == null) || (minAmountStr.isEmpty()) ? null
				: this.cpFMinAmount.getNumber();
		Number maxAmount = (maxAmountStr == null) || (maxAmountStr.isEmpty()) ? null
				: this.cpFMaxAmount.getNumber();
		String summary = this.cpFSummary.getText();
		String dr = this.cpFDR.getText();
		java.sql.Date date = data.getDate();
		float amount = data.getAmount();
		java.util.Date startTime;
		if ((startDate != null)
				&& (!date.after(startTime = startDate.getTime()))
				&& (!date.equals(startTime))) {
			return false;
		}
		java.util.Date endTime;
		if ((endDate != null) && (!date.before(endTime = endDate.getTime()))
				&& (!date.equals(endTime))) {
			return false;
		}

		if ((minAmount != null) && (amount < minAmount.floatValue())) {
			return false;
		}

		if ((maxAmount != null) && (amount > maxAmount.floatValue())) {
			return false;
		}

		if (this.cpFType.isVisible()) {
			JCCheckBoxList checkList = this.cpFType.getCheckedList();
			List<? extends Object> typeList = checkList.isSelectedEmpty() ? checkList
					.getAllItems() : checkList.getSelectedItems();

			if (!typeList.contains(data.getType())) {
				return false;
			}
		}

		if ((summary != null) && (!(summary = summary.trim()).isEmpty())
				&& (!data.getSummary().contains(summary))) {
			return false;
		}

		return (dr == null) || ((dr = dr.trim()).isEmpty())
				|| (data.getDetail().contains(dr))
				|| (data.getRemark().contains(dr));
	}

	public void submit() {
		if ((!this.btnSubmit.isVisible()) || (!checkInput())) {
			return;
		}

		String cmd = this.btnSubmit.getActionCommand();
		boolean insert = true;
		AllPay pay = null;
		String info = null;

		if ("Insert".equals(cmd)) {
			pay = new AllPay();
			pay.setRecordTime(new Timestamp(System.currentTimeMillis()));
			pay.setRecorder(this.currentUser.getName());
		} else if ("Modify".equals(cmd)) {
			pay = this.currentData;
			insert = false;
		}

		String oldType = pay.getType();
		resetRowValues(pay);
		String type = pay.getType();

		if (insert) {
			AllPayDAO.addAllPay(pay, this.categoryMap.get(type));

			if (checkFilter(pay)) {
				this.model.insertRowData(pay);
				int row = this.model
						.convertRowIndexesToView(new int[] { this.model
								.getRowCount() - 1 })[0];
				this.table.getSelectionModel().setSelectionInterval(row, row);
			} else {
				info = "添加成功，由于该记录不符合当前的查找条件，在表格中不会显示！";
			}

		} else {
			AllPayDAO.updateAllPay(pay);
			this.model.fireTableRowsUpdated(this.selectedIndex,
					this.selectedIndex);
			fillValues();

			if (!oldType.equals(type)) {
				CategoryDAO.afterRemoveItem(this.categoryMap.get(oldType));
				CategoryDAO.afterInsertItem(this.categoryMap.get(type));

				if (!checkFilter(pay)) {
					info = "修改成功，由于该记录不符合当前的查找条件，在表格中不会显示！";
				}
			}

		}

		resetButtonStatus(false);
		resetTextStatus(false);
		this.btnSubmit.setActionCommand(null);
		this.mgr.resetTotal();
		refreshStat();
		this.mgr.needReload(type);

		if ((!insert) && (!oldType.equals(type))) {
			this.mgr.needReload(oldType);
		}

		if (info != null) {
			JCMessageBox.createInformationMessageBox(
					this.keeper.getMainFrame(), insert ? "添加" : "修改", info)
					.open();

			if (insert) {
				this.table.clearSelection();
				clearValues();
			} else {
				this.model.delRowData(pay);
			}
		}
	}

	public void insertOrModify(boolean insert) {
		if (!this.bottom.isVisible()) {
			this.mgr.showDetail(true);
		}

		if (this.modifying) {
			cancel();
		}

		this.btnSubmit.setActionCommand(insert ? "Insert" : "Modify");
		resetTextStatus(true);
		resetButtonStatus(true);

		if (insert) {
			clearValues();
			this.cpDate.setDate(Calendar.getInstance());
			this.cpAmount.requestFocus();
		}
	}

	public void delete() {
		String message = "确定要删除该记录吗？";
		JCMessageBox box = JCMessageBox.createQuestionMessageBox(
				this.keeper.getMainFrame(), "删除", message);

		if (box.open() == 4) {
			String type = this.currentData.getType();
			AllPayDAO
					.deleteAllPay(this.currentData, this.categoryMap.get(type));
			this.model.delRowOnViewAt(this.selectedIndex);
			this.mgr.needReload(type);
			this.mgr.resetTotal();
			refreshStat();
		}
	}

	public void needReload() {
		this.inited = false;
	}

	public List<AllPay> getAllData() {
		return this.data;
	}

	public void exportToCSV() {
		JFileChooser chooser = new JFileChooser();
		String filterDes = "CSV (逗号分隔)(*.csv)";
		FileNameExtensionFilter filter = new FileNameExtensionFilter(filterDes,
				new String[] { "csv" });
		chooser.setFileFilter(filter);
		int ret = chooser.showSaveDialog(this.keeper.getMainFrame());

		if (ret != 0) {
			return;
		}

		File file = chooser.getSelectedFile();
		String path = file.getAbsolutePath();
		String info = null;

		if (!path.toLowerCase().endsWith(".csv")) {
			file = new File(path += ".csv");
		}

		try {
			IOUtil.writeToFile(file,
					Arrays.toString(COLUMNS_NAME).replaceAll("\\[|\\]| ", "")
							+ "\r\n", null, false);

			for (AllPay pay : this.data) {
				IOUtil.writeToFile(file, pay.toString() + "\r\n", null, true);
			}

			info = "<html>成功导出到文件：<br>" + path;
		} catch (IOException e) {
			info = "<html>导出时发生异常，请检查是否存在以下同名文件夹或同名只读文件！<br>" + path;
		} finally {
			if (info != null) {
				JCMessageBox.createInformationMessageBox(
						this.keeper.getMainFrame(), "导出", info).open();
			}
		}
	}

	public void updateCategory() {
	}

	public void refreshStat() {
	}

	public abstract String getType();

	private class DateListItem {
		private final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM");
		private Calendar calendar;
		private Calendar start;
		private Calendar end;

		public DateListItem(Calendar calendar) {
			this.calendar = calendar;
			this.start = Calendar.getInstance();
			this.end = Calendar.getInstance();
			this.start.clear();
			this.end.clear();
			this.start.set(calendar.get(1), calendar.get(2), 1);
			this.end.set(calendar.get(1), calendar.get(2) + 1, 1);
			this.end.add(14, -1);
		}

		public Calendar getStartDate() {
			return this.start;
		}

		public Calendar getEndDate() {
			return this.end;
		}

		@Override
		public String toString() {
			return this.FORMAT.format(this.calendar.getTime());
		}
	}

	private class SplitListener implements ItemListener {
		private SplitListener() {
		}

		public void itemStateChanged(ItemEvent e) {
			Object source = e.getSource();
			boolean selected = e.getStateChange() == 1;

			if (source == KeeperPane.this.btnShowLeft) {
				KeeperPane.this.setShowList(selected);
				KeeperPane.this.btnShowLeft.setToolTipText(selected ? "隐藏月份"
						: "显示月份");
			} else if (source == KeeperPane.this.btnShowRight) {
				KeeperPane.this.setShowStat(selected);
				KeeperPane.this.btnShowRight.setToolTipText(selected ? "隐藏统计"
						: "显示统计");
			}
		}
	}
}